#pragma once
#include<etcd/Value.hpp>
#include<etcd/Response.hpp>
#include<etcd/Client.hpp>
#include<etcd/KeepAlive.hpp>
#include<etcd/Watcher.hpp>
#include<unordered_map>
#include<unordered_set>
#include"log.hpp"
#include<string>
#include<thread>
#include<functional>
//////////////////////////////////////////////////
//注册中心，监控所有的服务: 当有新的服务添加 / 有服务失效
//设置对应的回调函数
//////////////////////////////////////////////////


namespace etcdtools
{
    using namespace Ns_Log;
    class RegistCenter
    {
        public:
        using func = std::function<void(const std::string&,const std::string&)>;
        private:
        void callback(const etcd::Response& resp)//回调函数
        {
            //对应事件更新对应容器
            if(!resp.is_ok())
            {
                std::cout<<"callback get a error resp"<<std::endl;
                return;
            }
            log(INFO,"收到事件通知");
            for(auto const& ev:resp.events())
            {
                if(ev.event_type()==etcd::Event::EventType::PUT)
                {
                    log(INFO,"服务信息发生更新: ");
                    if(ev.has_prev_kv())
                    {
                        log(INFO,"old service"+ev.prev_kv().key()+" : "+ev.prev_kv().as_string());
                        //删除容器中的服务
                        string service_name=ev.prev_kv().key();
                        service_name=service_name.substr(0,service_name.rfind('/'));

                        _del(service_name,ev.prev_kv().as_string());//从brpc信道管理器中删除
                    }
                    log(INFO,"new service online : "+ev.kv().key()+":"+ev.kv().as_string());
                    //添加到对应的容器当中
                    string service_name=ev.kv().key();
                    service_name=service_name.substr(0,service_name.rfind('/'));

                    _add(service_name,ev.kv().as_string());
                }
                else if(ev.event_type()==etcd::Event::EventType::DELETE_)
                {
                    log(INFO,"服务信息被删除: ");
                    log(INFO,"old service"+ev.prev_kv().key()+" : "+ev.prev_kv().as_string());
                    string service_name=ev.prev_kv().key();
                    service_name=service_name.substr(0,service_name.rfind('/'));

                    _del(service_name,ev.prev_kv().as_string());

                }
                else
                {
                    log(INFO,"服务信息无效: etcd error!");
                    log(INFO,"old service"+ev.prev_kv().key()+" : "+ev.prev_kv().as_string());
                    string service_name=ev.prev_kv().key();
                    service_name=service_name.substr(0,service_name.rfind('/'));
                    
                    _del(service_name,ev.prev_kv().as_string());
                }
            }
        }
        void threadroutine()
        {
            watcher.Wait();//创建一个线程来wait，而不是阻塞当前线程
        }
        //维护不同类型服务的容器，对外暴露，方便负载均衡模块使用
        etcd::Client cli;
        etcd::Watcher watcher;
        std::thread wait_thread;
        func _add;
        func _del;
        
        public:
        //初始化注册中心
        RegistCenter(const std::string& etcd_host,const std::string& dir,func add,func del)
        :cli(etcd_host),watcher(cli,dir,std::bind(&etcdtools::RegistCenter::callback,this,std::placeholders::_1),true),_add(add),_del(del)
        {
            //加载etcd中当前的所有服务
            auto resp=cli.ls(dir).get();
            if(!resp.is_ok())
            {
                log(ERROR,"服务获取失败: "+resp.error_message());
            }
            int sz=resp.keys().size();
            for(int i=0;i<sz;i++)
            {
                log(INFO,resp.value(i).as_string()+"可以提供"+resp.key(i)+" 服务");
                //将当前所有服务添加到分类的容器当中
                string service_name=resp.key(i);
                //切割得到服务前缀
                service_name=service_name.substr(0,service_name.rfind('/'));

                add(service_name,resp.value(i).as_string());//添加到brpc信道管理器中
            }
        }
        void Start()//??
        {
            wait_thread = std::thread(&etcdtools::RegistCenter::threadroutine, this);
            //wait_thread = std::thread([this]() { this->threadroutine(); });
        }
        ~RegistCenter()
        {
            wait_thread.join();            
        }
        
    };
}